import {
    AnchorSpec,
    Connection,
    ElementGroupRef,
    ElementId,
    Endpoint,
    EndpointSpec,
    EndpointOptions,
    jsPlumb,
    jsPlumbInstance, OverlaySpec, PaintStyle, OnConnectionBindInfo
} from "jsplumb";
import * as _ from 'lodash'
import elementType = JsPlumbLineType.elementType;
export default class JsPlumbLine {
    // 元素选中样式
    private _itemHoverClass='item-hover';
    // 自定义lable id
    private readonly _customOverlayId='customOverlay'

    //线上的lable
    private _labelOverlays={
        create:(component:Connection )=> {
            //todo 效果需要加
            let that=this;
            let btn=document.createElement("BUTTON");
            let t=document.createTextNode("CLICK ME");
            btn.addEventListener('click', event => {
                event.stopPropagation();
                console.log(event)
              console.log(component)

                that._delConn(component)
            });

            btn.appendChild(t);
            return btn
        },
        location:0.7,
        visible: false,
        id:this._customOverlayId
    }
    //删除连线之前方法
    private _beforeDelConnFun:any|null
    //删除连线之后方法
    private _delConnFun:any|null
    // 基本连接线样式
    public connectorPaintStyle = {
        // 线 颜色
        stroke: '#2f7ddb',
        // fill: 'transparent',
        // 连接器线条的宽度
        strokeWidth: 4
    }
    // 鼠标悬浮在连接线上的样式
    public connectorHoverStyle = {
        stroke: '#602177',
        strokeWidth: 6
        // fill: 'transparent'
    }
    // 空心圆端点样式设置
    public endpointStyle = { fill: '#5889f1', outlineStroke: '#61f504', outlineWidth: 1 }
    // 空心圆端点鼠标悬浮样式设置
    public endpointHoverStyle = { fill: 'red' }
    //
    public hollowCircle = {
        dragOptions: { cursor: 'pointer', zIndex: 9999 },
        endpoint: ['Dot', { radius: 7 }], // 端点的形状
        connectorStyle: this.connectorPaintStyle, // 连接线的颜色，大小样式
        connectorHoverStyle: this.connectorHoverStyle,
        // 端点的颜色样式
        paintStyle: this.endpointStyle,
        hoverPaintStyle: this.endpointHoverStyle,
        // anchor: "AutoDefault",
        isSource: true, // 是否可以拖动（作为连线起点）
        // 连接线的样式种类有[Bezier],[Flowchart],[StateMachine ],[Straight ]
        connector: ['Bezier', { stub: [0, 0], gap: 0, cornerRadius: 5, alwaysRespectStubs: false }],
        isTarget: true, // 是否可以放置（连线终点）
        maxConnections: -1, // 设置连接点最多可以连接几条线
        // 线样式 "Label" | "Arrow" | "PlainArrow" | "Custom"
        // connectorOverlays: [['Label', { width: 10, length: 10, location: 1 }]]
        connectorOverlays: [['Custom', this._labelOverlays]]
    } as EndpointOptions
    private _leftIds:Array<string> = []
    private _rightIds:Array<string> = []
    //线
    private _connects: Array<Array<string>>|null = []
    private  _plumbIns: jsPlumbInstance
    // uuid=>conn
    private _connectMap: any ={}
    // uuid=>labe状态
    private _connectLabe: any ={}

    constructor(options: JsPlumbLineType.JsPlumbLineOptions) {
        this._plumbIns = jsPlumb.getInstance()

        if(options.itemHoverClass){
            this._itemHoverClass = options.itemHoverClass
        }
        this._plumbIns.importDefaults({
            ConnectionsDetachable: false
        })
        this._initDate(options)
        this._initEvent(options)


    }

    /**
     * 初始化 数据 （元素 点 线）
     * @param options
     * @private
     */
    private _initDate(options: JsPlumbLineType.JsPlumbLineOptions){
        this._leftIds =  _.cloneDeep( options.leftIds);
        //左节点
        this._addElement(options.leftIds,['Right'] )
        //右节
        if(options.rightIds){
            this._rightIds =  _.cloneDeep(options.rightIds);
            this._addElement(options.rightIds,['Left'] )
        }
        //线
        if(options.connects){
            this._connects =  _.cloneDeep(options.connects);
            options.connects.forEach((row:Array<string>,index:number)=>{
                this._addConnect(row[0],row[1])
            })
        }
        //方法
        if(options.beforeDelConnFun){
            this._beforeDelConnFun = options.beforeDelConnFun
        }
        if(options.delConnFun){
            this._delConnFun = options.delConnFun
        }

    }
    /**
     * 初始化  所有操作事件
     * @param options
     * @private
     */
    private _initEvent(options: JsPlumbLineType.JsPlumbLineOptions){
        // 当前对象
        let that = this;

        // 连线事件-通知连接建立
        this._plumbIns.bind('connection', function(connInfo, originalEvent) {
            let uuid = that._connUuidToLeft(connInfo.connection.sourceId,connInfo.connection.targetId)
            let flag = true

            if (that._checkConn(connInfo.connection.sourceId,connInfo.connection.targetId)) {
                console.log('不能连接！')
                that._plumbIns.getRotation(connInfo.connection.sourceId)
                that._plumbIns.deleteConnection(connInfo.connection)
            }else {
                // 增加自定义方法
                if( options.beforeConnectionFun){
                    flag = options.beforeConnectionFun(uuid)
                }

                if(flag){
                    console.log("连接 %s===%s",uuid[0],uuid[1])
                    let old =  that._connects?.find((item)=>item[0]==uuid[0]&&item[1]==uuid[1]);
                    if(old==undefined||old?.length==0){
                        that._connects?.push(uuid)
                        that._connectMap[uuid[0]+'_'+uuid[1]]= connInfo.connection
                    }
                    connInfo.connection.hideOverlay(that._customOverlayId)
                    if(options.connectionFun){
                        options.connectionFun(uuid);
                    }
                }

            }
        })
        // 点击事件
        this._plumbIns.bind("click", function (conn, originalEvent) {
            that._chagneColorByConnection(conn)
            let uuid = that._connUuidToLeft(conn.sourceId,conn.targetId)

            if(options.clickConnectionFun){
                let uuidStr = that._connUuidToLeftStr(conn.sourceId,conn.targetId)

                options.clickConnectionFun(uuid,that._connectLabe[uuidStr] )
            }
        })
        // 线删除之后 还原样式
        this._plumbIns.bind('connectionDetached',function (connInfo, originalEvent) {
            console.log(connInfo)
            let end0 = connInfo.sourceEndpoint
            let end1 = connInfo.targetEndpoint
            end0.setPaintStyle(that.endpointStyle)
            end1.setPaintStyle(that.endpointStyle)
            let end0E = end0.getElement()
            let end1E = end1.getElement()
            end0E.className= _.replace(end0E.className, that._itemHoverClass+' ', '');
            end1E.className= _.replace(end1E.className, that._itemHoverClass+' ', '');
        })
    }

    /**
     * 通过线改变样式
     * @param conn
     * @private
     */
    private _chagneColorByConnection(conn:Connection){
        console.log('_chagneColorByConnection')
        let uuid = this._connUuidToLeftStr(conn.sourceId,conn.targetId)
        //改变lable
        let flag = this._getLableFlag(conn.sourceId,conn.targetId)
        if(flag){
            conn.hideOverlay(this._customOverlayId)
            this._connectLabe[uuid] = false
        }else {
            conn.showOverlay(this._customOverlayId)
            this._connectLabe[uuid] = true
        }
        //改变颜色
        let endpoints = conn.endpoints;
        let end0 = endpoints[0].getElement()
        let end1 = endpoints[1].getElement()
        if(this._connectLabe[uuid]){
            //选中样式
            conn.setPaintStyle(this.connectorHoverStyle)
            endpoints[0].setPaintStyle(this.endpointHoverStyle)
            endpoints[1].setPaintStyle(this.endpointHoverStyle)

            end0.className = this._itemHoverClass+' ' + end0.className
            end1.className = this._itemHoverClass+' ' + end1.className
        }else {
            conn.setPaintStyle(this.connectorPaintStyle)
            endpoints[0].setPaintStyle(this.endpointStyle)
            endpoints[1].setPaintStyle(this.endpointStyle)
            end0.className= _.replace(end0.className, this._itemHoverClass+' ', '');
            end1.className= _.replace(end1.className, this._itemHoverClass+' ', '');
        }
    }

    /**
     *  改变端点 样式
     * @param elementId 元素id
     * @param s    { stroke?: 外线色,fill?: 填充色,strokeWidth?: 外线宽度}
     * @param spec    端点的形状  ['Dot', { radius: 7 }]
     */
    public chagneColorByEndpoint(elementId:string,s: PaintStyle,spec?:EndpointSpec){
        let end = this._plumbIns.getEndpoint(elementId)
        if(spec){
            end.setEndpoint(spec)
        }
        end.setPaintStyle(s)
    }

    /**
     * 改变线的样式
     * @param elementId 元素id
     * @param s  默认{ stroke?: 线色,strokeWidth?: 线宽度}
     */
    public changeConnColor(elementId:string,s: PaintStyle){
        this._plumbIns.getAllConnections().forEach(conn=>{
            if(conn.targetId==elementId||conn.sourceId==elementId){
                conn.setPaintStyle(s)
            }
        })
    }

    /**
     * 选中一个元素
     * @param elementId
     */
    public selectElement(elementId:string){
        console.log("selectElement")
        let uuidStrs = this._connects?.filter(uuid=>uuid[0]==elementId||uuid[1]==elementId)
            .map(item=>item[0]+"_"+item[1]);
        uuidStrs?.forEach(uuid=>{
           let conn =  this._connectMap[uuid]
            if(conn){
                this._chagneColorByConnection(conn)
            }
        })
    }
    /**
     * 增加 右侧元素id
     * @param ids
     */
    public addRigthid(elementIds:Array<string>){
        this._rightIds?.push(...elementIds)
        this._addElement(elementIds,['Left'] )
    }
    /**
     * 移除
     * 注意 需要先删除元素之后，再能调方法，需要重新绘制线条和端点
     * @param elementIds
     * @param type
     */
    public removeElementIds(elementIds:Array<string>,type:elementType){
        //删除 线
        this._plumbIns.getAllConnections()
            .filter(item => elementIds.find( (row)=> row==item.targetId||row==item.sourceId))
            .forEach((conn)=>{
                this._delConn(conn)
            });
        //删除 锚点
        elementIds.forEach((item)=>{
            this._plumbIns.deleteEndpoint(item)
        })
        //删除 元素id
        if("left"==type){
            this._leftIds =  _.difference(this._leftIds, elementIds);
        }else {
            this._rightIds = _.difference(this._rightIds, elementIds);
        }
        this._plumbIns.repaintEverything(true)
    }
    private addConnect( leftElementId:string,rightElementId:string) {
        if(this._checkConn(leftElementId,rightElementId)){
            this._connects?.push([leftElementId,rightElementId])
            this._addConnect(leftElementId,rightElementId)
        }

    }

    public get leftIds(){
        return this._leftIds
    }
    public get rightIds(){
        return this._rightIds
    }
    public get connects(){
        return this._connects
    }
    public test(){
        console.log("_leftIds %o",this._leftIds)
        console.log("_rightIds %o",this._rightIds)
        console.log("_connects %o",this._connects)
        console.log("_connectMap %O",this._connectMap)
    }
    /**
     *
     * @param sourceId 线中的
     * @param targetId 线中的
     * @return [左元素id,右元素id]
     */
    private _connUuidToLeft(sourceId:string,targetId:string):string[]{
        let l = this._leftIds.filter((item=>item==sourceId))
        let uuid=[sourceId,targetId]
        if(l.length==0){
            uuid = uuid.reverse();
        }
        return uuid;
    }
    private _connUuidToLeftStr(sourceId:string,targetId:string):string{
        let uuid = this._connUuidToLeft(sourceId,targetId)
        return uuid[0]+'_'+uuid[1];
    }

    /**
     * 当前的 lable 显示状态
     * @param sourceId
     * @param targetId
     * @private
     */
    private _getLableFlag(sourceId:string,targetId:string):boolean {
        let uuid = this._connUuidToLeftStr(sourceId, targetId)
        let re = this._connectLabe[uuid]
        if (re == undefined || re == false) {
            re = false
        }
        return re;
    }




    /**
     * 添加元素
     * @param elementIds
     * @param anchor [Right Left]
     * @private
     */
    private _addElement(elementIds:Array<string>,anchor:AnchorSpec){
        elementIds.forEach((item)=>{
            this.addEndpoint(item,  anchor)
        })
    }

    /**
     * 对比点是同一组 并不是自己 不存在 才能连线
     * @param leftElementId
     * @param rightElementId
     * @private
     */
    private _checkConn( leftElementId:string,rightElementId:string):boolean{
        //同组
        let leftgroup =  this._leftIds.filter((item)=>item==leftElementId||item==rightElementId)
        let rightgroup =  this._rightIds.filter((item)=>item==leftElementId||item==rightElementId)
        console.log("leftgroup=%i   rightgroup=%i",leftgroup,rightgroup)
        //是否存在
        let old =  this._connects?.find((item)=>item[0]==leftElementId&&item[1]==rightElementId)
        let flag = old!=undefined&&old.length>0

        return leftgroup?.length==2||rightgroup?.length==2 ||leftElementId == rightElementId||flag
    }


    /**
     * 删除线
     * _beforeDelConnFun
     *   deleteConnection
     *   _connectMap
     *   _connects
     *   _delConnFun
     * @param conn
     * @private
     */
    private _delConn(conn:Connection){
        let uuid = this._connUuidToLeft(conn.sourceId,conn.targetId);
        let delCheckFlag = true;
        if(this._beforeDelConnFun){
            delCheckFlag =  this._beforeDelConnFun(uuid)
        }
        if(delCheckFlag){
            this._plumbIns.deleteConnection(conn)
            _.unset(this._connectMap, uuid[0]+"_"+uuid[1]);
            _.remove(this._connects as string[][], function(n) {
                return n[0] == uuid[0]&&n[1]==uuid[1]
            })
            if(this._delConnFun){
                this._delConnFun(uuid)
            }
        }

    }
    /**
     * 字符型转 ElementGroupRef
     * @param elementIds
     */
    private _covId(elementIds:Array<string>):ElementGroupRef{
        return elementIds.map((item,index):ElementId=>{
            return item as ElementId
        })
    }
    //
    private getConnects(elementId:string){
        return this._connects;
    }


    /**
     * 线上的label
     * @param leftElementId
     * @param rightElementId
     * @private
     */
    private _addCustomLable( leftElementId:string,rightElementId:string):Array<OverlaySpec>{
        let labelOverlays =  _.cloneDeep(this._labelOverlays);
        labelOverlays.id=leftElementId+"_"+rightElementId
        return [['Custom', labelOverlays]] as Array<OverlaySpec>
    }
    /**
     * 添加线
     * @param leftElementId
     * @param rightElementId
     */
    private _addConnect( leftElementId:string,rightElementId:string){

        let conn = this._plumbIns.connect({
            // 对应上述基本概念
            source: leftElementId,
            target: rightElementId,
            // anchor: ['Left', 'Right', 'Top', 'Bottom', [0.3, 0, 0, -1], [0.7, 0, 0, -1], [0.3, 1, 0, 1], [0.7, 1, 0, 1]],
            anchor: ['Left', 'Right'],
            connector: this.hollowCircle.connector ,
            endpoint: this.hollowCircle.endpoint,
            overlays: this.hollowCircle.connectorOverlays, // overlay
            // overlays: this._addCustomLable(leftElementId,rightElementId), // overlay
            // 添加样式
            paintStyle: this.connectorPaintStyle, // connector
            hoverPaintStyle: this.connectorHoverStyle,
            endpointStyle: this.endpointStyle, // endpoint
            endpointHoverStyle: this.endpointHoverStyle,
        })
        conn.hideOverlay(this._customOverlayId)

        this._connectMap[leftElementId+"_"+rightElementId] = conn;
    }

    /**
     * 增加锚点
     * @param elementId  元素id
     * @param anchor 锚点位置 Right Left
     */
    private addEndpoint(elementId:string,anchor:AnchorSpec){
        // 添加连接点
        this._plumbIns.addEndpoint(elementId, { anchor: anchor, maxConnections: -1, uuid: elementId },  this.hollowCircle)
    }

}
